`include "mycpu.h"

module mem_stage(
    input                          clk           ,
    input                          reset         ,
    //allowin
    input                          ws_allowin    ,
    output                         ms_allowin    ,
    //from es
    input                          es_to_ms_valid,
    input  [`ES_TO_MS_BUS_WD -1:0] es_to_ms_bus  ,
    input                          ws_flush      ,
    input                          c0_status_exl ,
    //to es
    output [`MS_TO_ES_BUS_WD -1:0] ms_to_es_bus  ,
    //to ws
    output                         ms_to_ws_valid,
    output [`MS_TO_WS_BUS_WD -1:0] ms_to_ws_bus  ,
    //������ˮ�߳�ͻ
    output [`MS_CONFLICT_BUS_WD -1:0] ms_conflict_bus,
    //from data-sram
    input                          data_sram_data_ok,
    input  [31                 :0] data_sram_rdata
);

reg         ms_valid;
wire        ms_ready_go;

/* privileged instruction */
wire [7:0] ms_c0_addr;
wire ms_op_mfc0;
wire ms_op_mtc0;
wire eret_flush;
wire es_ex;
wire ms_ex;
wire [4:0] es_excode;
wire [4:0] ms_excode;
wire bd;
wire ms_op_tlbwi;
wire ms_op_tlbr;
wire ms_op_tlbp;
wire inst_retake;
wire ms_tlb_refill;

//data sram
wire        load_store;
wire [31:0] rdata;
reg         rdata_reg_valid;
reg  [31:0] rdata_reg;
reg         cancel;

reg [`ES_TO_MS_BUS_WD -1:0] es_to_ms_bus_r;
wire [31:0] ms_rt_value;
wire [ 6:0] ms_load_type;
wire        es_res_from_mem;
wire        ms_res_from_mem;
wire        ms_gr_we;
wire [ 4:0] ms_dest;
wire [31:0] ms_alu_result;
wire [31:0] ms_pc;
assign {ms_tlb_refill  ,  //133:133
        inst_retake    ,  //132:132
        ms_op_tlbp     ,  //131:131
        ms_op_tlbr     ,  //130:130
        ms_op_tlbwi    ,  //129:129
        load_store     ,  //128:128
        bd             ,  //127:127
        es_excode      ,  //126:122
        es_ex          ,  //121:121
        eret_flush     ,  //120:120
        ms_op_mfc0     ,  //119:119
        ms_op_mtc0     ,  //118:118
        ms_c0_addr     ,  //117:110
        ms_rt_value    ,  //109:78
        ms_load_type   ,  //77:71
        es_res_from_mem,  //70:70
        ms_gr_we       ,  //69:69
        ms_dest        ,  //68:64
        ms_alu_result  ,  //63:32
        ms_pc             //31:0
       } = es_to_ms_bus_r;

wire [ 1:0] load_offset;
wire [ 3:0] load_select;
wire [ 7:0] load_byte;
wire [15:0] load_half;
wire [31:0] load_left;
wire [31:0] load_right;
wire [31:0] mem_result;
wire [31:0] ms_final_result;

assign ms_to_ws_bus = {ms_tlb_refill  ,  //92:92
                       inst_retake    ,  //91:91
                       ms_op_tlbp     ,  //90:90
                       ms_op_tlbr     ,  //89:89
                       ms_op_tlbwi    ,  //88:88
                       bd             ,  //87:87
                       ms_excode      ,  //86:82
                       ms_ex          ,  //81:81
                       eret_flush     ,  //80:80
                       ms_op_mfc0     ,  //79:79
                       ms_op_mtc0     ,  //78:78
                       ms_c0_addr     ,  //77:70
                       ms_gr_we       ,  //69:69
                       ms_dest        ,  //68:64
                       ms_final_result,  //63:32
                       ms_pc             //31:0
                      };

assign ms_ready_go    = !load_store || !cancel && (data_sram_data_ok || rdata_reg_valid);
assign ms_allowin     = !ms_valid || ms_ready_go && ws_allowin;
assign ms_to_ws_valid = ms_valid && ms_ready_go;
always @(posedge clk) begin
    if (reset) begin
        ms_valid <= 1'b0;
    end
    else if (ws_flush) begin
        ms_valid <= 1'b0;
    end
    else if (ms_allowin) begin
        ms_valid <= es_to_ms_valid;
    end

    if (ws_flush)
        es_to_ms_bus_r <= 134'd0;
    else if (es_to_ms_valid && ms_allowin) 
        es_to_ms_bus_r <= es_to_ms_bus;
end

assign load_offset = ms_alu_result[1:0];
assign load_select[0] = (load_offset == 2'b00);
assign load_select[1] = (load_offset == 2'b01);
assign load_select[2] = (load_offset == 2'b10);
assign load_select[3] = (load_offset == 2'b11);
assign rdata = rdata_reg_valid? rdata_reg : data_sram_rdata;
assign load_byte = {8{load_select[0]}} & rdata[ 7: 0]
                 | {8{load_select[1]}} & rdata[15: 8]
                 | {8{load_select[2]}} & rdata[23:16]
                 | {8{load_select[3]}} & rdata[31:24];
assign load_half = {16{load_select[0]}} & rdata[15: 0]
                 | {16{load_select[2]}} & rdata[31:16];
assign load_left = {32{load_select[0]}} & {rdata[ 7:0], ms_rt_value[23:0]}
                 | {32{load_select[1]}} & {rdata[15:0], ms_rt_value[15:0]}
                 | {32{load_select[2]}} & {rdata[23:0], ms_rt_value[ 7:0]}
                 | {32{load_select[3]}} &  rdata[31:0];
assign load_right = {32{load_select[0]}} &  rdata[31:0]
                  | {32{load_select[1]}} & {ms_rt_value[31:24], rdata[31:8]}
                  | {32{load_select[2]}} & {ms_rt_value[31:16], rdata[31:16]}
                  | {32{load_select[3]}} & {ms_rt_value[31: 8], rdata[31:24]};
assign mem_result = {32{ms_load_type[0]}} & {{24{load_byte[ 7]}}, load_byte[ 7:0]}
                  | {32{ms_load_type[1]}} & {24'd0              , load_byte[ 7:0]}
                  | {32{ms_load_type[2]}} & {{16{load_half[15]}}, load_half[15:0]}
                  | {32{ms_load_type[3]}} & {16'd0              , load_half[15:0]}
                  | {32{ms_load_type[4]}} & rdata
                  | {32{ms_load_type[5]}} & load_left
                  | {32{ms_load_type[6]}} & load_right;

assign ms_res_from_mem = es_res_from_mem && !ms_ex;
assign ms_final_result = {32{ms_res_from_mem}} & mem_result | {32{!ms_res_from_mem}} & ms_alu_result;
//������ˮ�߳�ͻ
wire  ms_conflict_valid;
assign ms_conflict_valid = ms_gr_we && ms_valid;
assign ms_conflict_bus = {ms_op_mfc0, ms_ready_go, ms_conflict_valid, ms_dest, ms_final_result};

/* privileged instruction */
wire ms_flush;
wire ms_entryhi_we;
assign ms_ex = es_ex;
assign ms_excode = es_excode;
assign ms_flush = eret_flush | (ms_ex & ~c0_status_exl) | (inst_retake & ms_valid);
assign ms_entryhi_we = ms_op_mtc0 && (ms_c0_addr == `CR_ENTRYHI) && ms_valid;
assign ms_to_es_bus = {ms_entryhi_we, ms_flush};

//data sram
always @(posedge clk) begin
    if(reset || ws_flush)
        rdata_reg_valid <= 1'b0;
    else if(load_store && ms_ready_go && !ws_allowin)
        rdata_reg_valid <= 1'b1;
    else if(ws_allowin)
        rdata_reg_valid <= 1'b0;

    if(ms_ready_go && !ws_allowin && !rdata_reg_valid)
        rdata_reg <= data_sram_rdata;
end
always @(posedge clk) begin
    if(reset)
        cancel = 1'b0;
    else if(ws_flush && (es_to_ms_bus[128] || load_store && !ms_allowin && !ms_ready_go))
        cancel = 1'b1;
    else if(data_sram_data_ok)
        cancel = 1'b0;
end

endmodule
